package jwebtester.business.beans.competencies;

import infrastructure.Scopes;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import jwebtester.business.services.CompetenciesService;
import jwebtester.business.services.SessionMapService;
import jwebtester.business.utils.FacesHelper;
import jwebtester.dal.provider.CompetenciesDataProvider;

import org.primefaces.event.NodeCollapseEvent;
import org.primefaces.event.NodeExpandEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import domain.entities.Competency;

@Component
@Scope(Scopes.Request)
public class CompetenciesBean {

	private static final Logger LOG = LoggerFactory.getLogger(CompetenciesBean.class);

	@Autowired
	private transient SessionMapService sessionService;

	@Autowired
	private transient CompetenciesService competenciesService;

	@Autowired
	private transient CompetenciesDataProvider dataProvider;

	@Autowired
	private transient EditCompetencyBean editCompetencyBean;

	@Autowired
	private transient NewCompetencyBean newCompetencyBean;
	
	@Autowired
	private transient DeleteCompetencyBean deleteCompetencyBean;

	private transient TreeNode root;

	private transient TreeNode selectedNode;

	private transient Competency selectedCompetency;

	public CompetenciesBean() {
		LOG.trace(String.format("%s bean constructed", CompetenciesBean.class));
	}

	public TreeNode getRoot() {
		if (root == null) {
			root = buildVisualTree();
		}

		return root;
	}

	public TreeNode getSelectedNode() {
		return selectedNode;
	}

	public void setSelectedNode(final TreeNode selectedNode) {
		this.selectedNode = selectedNode;

		if (selectedNode != null) {
			selectedCompetency = (Competency) selectedNode.getData();
		}
	}

	public Competency getSelectedCompetency() {
		return selectedCompetency;
	}

	public void beforeNewRootCompetency() {
		newCompetencyBean.beforeCreateNewCompetency(null, false);
	}

	public void beforeNewCompetency() {
		newCompetencyBean.beforeCreateNewCompetency(selectedCompetency, true);
	}

	public void createNewCompetency() {
		newCompetencyBean.createNewCompetency();
		refresh();
	}

	public void beforeUpdateCompetency() {
		if (checkCompetencySelected()) {
			editCompetencyBean.beforeUpdateCompetency(selectedCompetency);
		}
	}

	public void updateCompetency() {
		editCompetencyBean.updateCompetency();
		refresh();
	}
	
	public void beforeRemoveCompetency(){
		if (checkCompetencySelected()){
			deleteCompetencyBean.beforeRemoveCompetency(selectedCompetency);
		}
	}
	
	public void remove(){
		deleteCompetencyBean.remove(selectedCompetency);
		refresh();
	}

	public void refresh() {
		LOG.trace("Refreshing competencies tree");

		loadTree(true);
		root = buildVisualTree();
	}

	public void onNodeCollapse(final NodeCollapseEvent event) {
		final TreeNode node = event.getTreeNode();
		final Competency competency = (Competency) node.getData();

		final Map<Integer, Boolean> expandedNodesMaps = competenciesService.getExpandedNodesMap();
		expandedNodesMaps.put(Integer.valueOf(competency.getId()), Boolean.FALSE);
	}

	public void onNodeExpand(final NodeExpandEvent event) {
		final TreeNode node = event.getTreeNode();
		final Competency competency = (Competency) node.getData();

		final Map<Integer, Boolean> expandedNodesMaps = competenciesService.getExpandedNodesMap();
		expandedNodesMaps.put(Integer.valueOf(competency.getId()), Boolean.TRUE);
	}

	private TreeNode buildVisualTree() {

		LOG.trace("Building UI tree");

		TreeNode uiTreeRoot = null;
		final Map<Integer, Boolean> expandedNodesMaps = competenciesService.getExpandedNodesMap();

		try {

			final Competency tree = loadTree(false);
			uiTreeRoot = new DefaultTreeNode(tree, null);

			final List<Competency> rootCompetencies = tree.getChilds();
			if (rootCompetencies != null) {

				tree.setChilds(rootCompetencies);

				final Map<Integer, TreeNode> uiCache = new HashMap<Integer, TreeNode>();

				final Stack<Competency> stack = new Stack<Competency>();
				stack.push(tree);

				while (stack.size() > 0) {
					final Competency top = stack.pop();
					final List<Competency> childs = top.getChilds();
					for (Competency c : childs) {
						stack.push(c);

						TreeNode parentUiNode = uiCache.get(c.getParentID());
						if (parentUiNode == null) {
							parentUiNode = uiTreeRoot;
						}

						final TreeNode node = new DefaultTreeNode(c, parentUiNode); //NOPMD

						boolean expanded = false;
						final Integer competencyID = Integer.valueOf(c.getId());
						if (expandedNodesMaps.containsKey(competencyID)) {
							expanded = expandedNodesMaps.get(competencyID);
						}
						node.setExpanded(expanded);

						uiCache.put(c.getId(), node);
					}
				}
			}
		} catch (Exception ex) {
			LOG.error("Error fetching root nodes for competencies tree", ex);
		}

		return uiTreeRoot;
	}

	private boolean checkCompetencySelected() {
		boolean result = true;
		if (notSelectedCompetency()) {
			new FacesHelper().error("Error", "Competency hasn't been selected. "
					+ "Please select competency to edit / remove it");
			result = false;
		}

		return result;
	}

	private boolean notSelectedCompetency() {
		return selectedCompetency == null;
	}

	private Competency loadTree(final boolean forceRefresh) {

		LOG.trace("Trying to get cached competencies tree");

		final int organizationID = sessionService.getCurrentOrganizationID();
		return competenciesService.getHierarchy(organizationID, forceRefresh);
	}
}
